<!DOCTYPE html> 
<html>
<head>
<title>Skyline75489</title>
<meta charset='utf-8'>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<link rel="stylesheet" href="../static/styles/github.css">
<link rel="stylesheet" href="../static/post.css">
</head>
<body>

<div class="wrapper">
<div class="header">
	<span class="blog-name">Skyline75489</span>

<a class="nav" href="../index.html">Home</a>
<a class="nav" href="about.html">About</a>
</div>
<div class="content">

<h1>Swift学习笔记(五)——类型系统初探</h1>
<h3>从头学习 Swift</h3>
<p>尽管 Swift 的语法参考了 Python，JavaScript 等动态类型语言，然而 Swift 是一个不折不扣的<strong>强类型</strong>，<strong>静态类型</strong>语言。Swift 对于类型的检查非常严格，以至于让我这样写熟了 Python 的人写起来非常不适应 :(</p>
<pre><code class="lang-swift">var a = 10; // 自动类型推导
a = &quot;Hello&quot; // 报错，类型不匹配
</code></pre>
<p>因为 Swift 的类型特性，想用写 Python 和 JS 的思想去写 Swift 是不靠谱的，大量的编译报错会把人弄崩溃。同时，用写 OC 的思想去写 Swift 也会被坑。</p>
<pre><code class="lang-swift">let clz = NSClassFromString(name) as! NSObject.Type
let instance = clz.init() 
// NSClassFromString 返回值为 AnyClass
// 想调用 init 必须要强制 cast 成 NSObject
// 然而强制 cast 本身又是不被推荐的
</code></pre>
<p>看来想达到“熟悉Swift”，需要从头开始理解 Swift，并且形成一种独立的编程思想（当初是谁说 Swift 大幅降低 iOS 学习成本的，分明是提高了好吗...）。</p>
<h3>Optional 与 AnyObejct 的纠缠</h3>
<p>Optional 类型是 Swift 中非常亮眼的特性，我在 <a href="https://skyline75489.github.io/post/2014-12-25_swift-learning-part-1.html">之前的博文</a> 中也提到过。当变量的类型本身是确定的情况下，Optional 是很好用的。但是当变量类型是 AnyObject? 时，使用 Optional 就不可避免的要引入类型转换了。在使用旧的 OC API 时会碰到很多这种情况：</p>
<pre><code class="lang-swift">let d = NSMutableDictionary()
let r = d[&quot;Something&quot;] // 类型为 AnyObject?

// 推荐这样进行类型转换，以防止强转失败
if let r2 = d[&quot;Other&quot;] as? String {
   // Do something
}
</code></pre>
<p>当大量出现 AnyObject? 时，代码会变得非常啰嗦。<a href="https://github.com/SwiftyJSON/SwiftyJSON">SwiftyJSON</a>的 README 中展示了一个例子：</p>
<pre><code class="lang-swift">if let JSONObject = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments) as? [[String: AnyObject]],
    let username = (JSONObject[0][&quot;user&quot;] as? [String: AnyObject])?[&quot;name&quot;] as? String {
        // There&#39;s our username
}
</code></pre>
<p>正如 <a href="http://swifter.tips/any-anyobject/">SwifterTips 中的这篇文章</a> 提到的，AnyObject（以及 Any，AnyClass）等本身就是为了向 OC 类型提供兼容而出现的妥协产物。然而 Swift 语言本身还在发展进步中，大家抛弃 OC 的步伐也没有想象中那么快。因此，尽管有了 <a href="https://github.com/SwiftyJSON/SwiftyJSON">SwiftyJSON</a> 这样方便大家的开源库，在可预见的未来里，上面例子中的痛点在 Swift 开发中依然会存在。</p>
<h3>泛型与 Extension 的矛盾</h3>
<p>在最新的 Objective-C 2.0 里苹果引入了轻量级泛型的支持，仅仅是语法层次上的静态检查，已经是 OC 开发者的一大福音了：</p>
<pre><code class="lang-objectivec">@property (nonatomic) NSArray&lt;Person *&gt; *persons;
</code></pre>
<p>Swift 中泛型支持已经是一个单独的特性了，和 OC 相比是一大进步：</p>
<pre><code class="lang-swift">struct Stack&lt;Element&gt; {
    var items = [Element]()
    mutating func push(item: Element) {
        items.append(item)
    }
    mutating func pop() -&gt; Element {
        return items.removeLast()
    }
}
</code></pre>
<p>同时 Swift 中还支持对泛型类型进行限制，例如 Dictionary 的 key 类型限制为 Hashable：</p>
<pre><code class="lang-swift">public struct Dictionary&lt;Key : Hashable, Value&gt; : CollectionType, DictionaryLiteralConvertible {
   // ...
}
</code></pre>
<p>这个特性很先进，而且很好用（对比 C++...）。</p>
<p>在泛型的诸多好用之处以外，和 Extension 的混用应该算是一个不大不小的痛点。</p>
<p>例如我们试着给 Array 类型加一个 Extension：</p>
<pre><code class="lang-swift">extension Array {
    mutating func someFunc() {
        self.append(&quot;Hello&quot;)
    }
}
</code></pre>
<p>上面的代码会报 <code>Cannot invoke 'append' with an argument list of type '(String)'</code>。这里不管 append 什么类型都会报错，因为 Array 本身是一个泛型类型，不确定的类型是没法随便 append 的。</p>
<p>正确的写法应该是这样的：</p>
<pre><code class="lang-swift">extension Array where Element:StringLiteralConvertible {
    mutating func someFunc() {
        self.append(&quot;Hello&quot;)
    }
}
</code></pre>
<p>同理，如果想添加别的类型的支持（例如整数类型），就要写在另一个 Extension 里，并且加上类型限制：</p>
<pre><code class="lang-swift">extension Array where Element: IntegerLiteralConvertible {
    mutating func otherFunc() {
        self.append(188)
    }
}
</code></pre>
<p>在给泛型类型添加 Extension 时仍然需要确定泛型的具体类型，其实等于是丧失了 Extension 的灵活性。这里的矛盾也可以看成是旧特性（OC 的 Category &amp; Extension）与新特性（泛型）之间的一种妥协。</p>
<p>希望苹果在 Swift 的下个版本中能对这篇文章里提到的这些痛点给出更好的解决方案。如果你知道更好的写法的话，欢迎随时骚扰 :)</p>


</div>
</div>
<script src="../static/highlight.pack.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
</body>

</html>
